// 7个点位 点击每个点位跳转到对于模型中的位置。点位中有报警信息（0,1表示），报警的标红，显示报警信息
<template>
  <div ref="webgl"></div>
  <div class="contianer" v-if="showInfo">
    <div class="floor-contianer">
      <div v-for="item in mainModel" :key="item.id" @click="changeFloor(item.id)">
        <el-button :type="item.id === 4 ? 'danger' : ''">{{ item.name }}</el-button>
      </div>
      <div class="btns" v-if="floorIndex === 0">
        <el-button @click="changeModel('parking')">停车场</el-button>
        <el-button @click="changeModel('index')">首页</el-button>
        <el-button @click="changeModel('warehouse')">仓库</el-button>
      </div>

      <el-card class="car-model" v-if="showCar">
        <div>
          <span style="margin-left: 40px">警报 </span>
          <el-icon style="position: absolute; right: 0px; top: 10px" @click="showCar = false"
            ><Close
          /></el-icon>
        </div>
        <div style="font-size: 12px; color: #999; margin-top: 10px">
          警报警报警报警报警报警报警报警报警报警报警报警报警报
        </div>
      </el-card>
    </div>
  </div>
  <!-- B1楼 -->
  <!-- <div class="B1-contaner" v-if="floorIndex === 1"> -->
  <div class="B1-contaner">
    <div v-for="item in videoList" :key="item.id">
      <el-button
        text
        :type="item.id === 4 ? 'danger' : 'primary'"
        class="videos"
        @click="showVideo(item)"
        >{{ item.video_name }}</el-button
      >
    </div>
  </div>
  <div class="percentage" v-if="showPercentage">
    <div style="margin-left: 30%">模型加载中...</div>
    <el-progress :percentage="percentage" :indeterminate="true" />
  </div>
</template>

<script setup>
import { ref, onMounted, reactive, watch } from 'vue'
import { defineProps, defineEmits } from 'vue'
import * as THREE from 'three'
import TWEEN from '@tweenjs/tween.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js'
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'
const videoList = ref([])
const currentIndex = ref(0)
const tags = ref([]) // 存储标签
const props = defineProps({
  videoInfo: Array,
  tallList: Array
})
const emit = defineEmits(['getPosition'])

const clientWidth = window.innerWidth
const clientHeight = window.innerHeight
let offsetLeft = 0
let offsetTop = 0

const webgl = ref()
const showCar = ref(false)
const showInfo = ref(false)
let percentage = ref(0)
const showPercentage = ref(true)
const floorIndex = ref(0)

let camera, scene, renderer, composer, outlinePass, css2DRenderer, parentObject

const mainModel = [
  {
    name: '整体',
    url: './static/model/DL/DL.gltf',
    id: 0,
    cameraPosition: { x: -41.84, y: 8.44, z: 114.57 },
    cameraLookAt: { x: 0, y: 0, z: 0 }
  },
  {
    name: 'B1',
    url: './static/model/B1/B1.gltf',
    id: 1,
    cameraPosition: { x: -14.016, y: 83.24, z: 83.24 },
    cameraLookAt: { x: 0, y: 0, z: 0 }
  },
  {
    name: 'F1',
    url: './static/model/B2/B2.gltf',
    id: 2,
    cameraPosition: { x: -14.016, y: 83.24, z: 83.24 },
    cameraLookAt: { x: 0, y: 0, z: 0 }
  },
  {
    name: 'F2',
    url: './static/model/F2/F2.gltf',
    id: 3,
    cameraPosition: { x: -14.016, y: 83.24, z: 83.24 },
    cameraLookAt: { x: 0, y: 0, z: 0 }
  },
  {
    name: 'F3',
    url: './static/model/F3/F3.gltf',
    id: 4,
    cameraPosition: { x: -14.016, y: 83.24, z: 83.24 },
    cameraLookAt: { x: 0, y: 0, z: 0 }
  }
]

onMounted(() => {
  initModel()
})

watch(
  () => props.videoInfo,
  (newVal) => {
    videoList.value = newVal
  },
  { immediate: true }
)

watch(
  () => props.tallList,
  (newVal) => {
    newVal.forEach((point) => {})
  }
  // { immediate: true }
)
function clearTags() {
  tags.value.forEach((tag) => {
    if (tag.parent) {
      tag.parent.remove(tag) // 从父对象中移除标注
    } else {
      scene.remove(tag) // 从场景中移除标注
    }
  })
  tags.value = [] // 清空存储的标注
}
// 创建标签
function createTag(parentObject, point, worldPosition) {
  clearTags()
  if (point && point.status == 2) {
    showCar.value = true
  }
  const tagElement = document.createElement('div')
  tagElement.className = 'tag'

  const imgElement = document.createElement('img')
  imgElement.src = '../../../public/static/location.png'
  imgElement.style.width = '30px'

  imgElement.onload = () => {
    const tag = new CSS2DObject(tagElement)
    // tag.position.set(0, 1, 0); // 调整标注位置
    if (point) {
      tag.position.set(point.tall, point.wide, point.long) // 设置标注位置为坐标点
    }

    // 将标注存储到 tags 数组中
    tags.value.push(tag)

    if (parentObject) {
      const params = {
        x: worldPosition.x,
        y: worldPosition.y,
        z: worldPosition.z,
        currentIndex: currentIndex.value
      }
      emit('getPosition', params)
      parentObject.add(tag)
    } else {
      scene.add(tag)
    }
  }

  tagElement.appendChild(imgElement)
}
function showVideo(index) {
  props.tallList.forEach((item) => {
    if (item.video_id == index.id) {
      changeFloor(index.id)
      currentIndex.value = index.id
    }
  })
}

// 楼层切换
function changeFloor(index) {
  showCar.value = false
  currentIndex.value = floorIndex.value = index
  // 清除之前的标注
  clearTags()

  // 创建当前楼层的标注
  props.tallList.forEach((item) => {
    if (item.video_id == index) {
      createTag('', item, '')
    }
  })

  // 获取当前楼层的相机位置和朝向
  const selectedModel = mainModel.find((model) => model.id === index)
  if (selectedModel) {
    const { cameraPosition, cameraLookAt } = selectedModel

    // 平滑过渡到新的相机位置和朝向
    new TWEEN.Tween(camera.position)
      .to(cameraPosition, 1000)
      .easing(TWEEN.Easing.Quadratic.Out)
      .start()

    new TWEEN.Tween(camera.rotation)
      .to(new THREE.Vector3().setFromEuler(new THREE.Euler()).normalize(), 1000)
      .easing(TWEEN.Easing.Quadratic.Out)
      .onUpdate(() => {
        camera.lookAt(new THREE.Vector3(cameraLookAt.x, cameraLookAt.y, cameraLookAt.z))
      })
      .start()
  }

  // 显示当前楼层的模型
  scene.traverse((child) => {
    if (child.isGroup && child.userData.id !== undefined) {
      child.visible = child.userData.id === index
    }
  })
}
// 整体模型切换
function changeModel(model) {
  if (!camera) return
  if (model === 'index') {
    camera.position.set(-41.84, 8.44, 114.57)
    camera.lookAt(new THREE.Vector3(0, 0, 0))
  }
  if (model === 'parking') {
    camera.position.set(45, 1, 110)
    camera.lookAt(new THREE.Vector3(0, 0, 0))
  }
  if (model === 'warehouse') {
    camera.position.set(150, 20, -50)
    camera.lookAt(new THREE.Vector3(0, 0, 0))
  }
}

// 初始化模型
function initModel() {
  updateContainerElement(webgl.value)

  // 创建场景
  scene = new THREE.Scene()
  scene.background = new THREE.Color(0x8b9290)

  // 创建相机
  camera = new THREE.PerspectiveCamera(45, clientWidth / clientHeight, 1, 100000)
  camera.position.set(-41.84, 8.44, 114.57)
  camera.lookAt(new THREE.Vector3(0, 0, 0))

  // 创建渲染器
  renderer = new THREE.WebGLRenderer()
  renderer.setSize(clientWidth, clientHeight)
  webgl.value?.appendChild(renderer.domElement)

  // 创建 CSS2DRenderer
  css2DRenderer = new CSS2DRenderer()
  css2DRenderer.setSize(clientWidth, clientHeight)
  css2DRenderer.domElement.style.position = 'absolute'
  css2DRenderer.domElement.style.top = '0'
  css2DRenderer.domElement.style.pointerEvents = 'none' // 防止遮挡点击事件
  webgl.value?.appendChild(css2DRenderer.domElement)

  // 添加环境光
  const light = new THREE.HemisphereLight(0xffffff, 0xcccccc, 1)
  scene.add(light)

  // 添加平行光
  const directionalLight = new THREE.DirectionalLight(0xffffff, 4)
  directionalLight.position.set(1, 1, 1).normalize()
  scene.add(directionalLight)

  // 添加控制器
  const controls = new OrbitControls(camera, renderer.domElement)

  // 组合器
  composer = new EffectComposer(renderer)
  const renderPass = new RenderPass(scene, camera)
  composer.addPass(renderPass)

  // 模型外边框
  outlinePass = new OutlinePass(new THREE.Vector2(clientWidth, clientHeight), scene, camera)
  outlinePass.edgeStrength = 5
  outlinePass.edgeGlow = 1
  outlinePass.pulsePeriod = 2
  outlinePass.visibleEdgeColor.set('#0B33EC')
  outlinePass.hiddenEdgeColor.set('#3449A9')
  composer.addPass(outlinePass)

  // 抗锯齿
  const effectFXAA = new ShaderPass(FXAAShader)
  effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight)
  composer.addPass(effectFXAA)
  const raycaster = new THREE.Raycaster()

  webgl.value.addEventListener(
    'click',
    (event) => {
      onClick(event, raycaster)
    },
    false
  )
  // webgl.value.addEventListener('click', onClick, false)
  loadAllModel()
  function animate() {
    TWEEN.update()
    requestAnimationFrame(animate)
    renderer.render(scene, camera)
    composer.render()
    css2DRenderer.render(scene, camera) // 渲染 CSS2D 标注
  }
  animate()

  setupWindowResizeHandler(camera, renderer)
}

function onClick(event, raycaster) {
  const mouse = new THREE.Vector2()
  event.preventDefault()
  mouse.x = ((event.clientX - offsetLeft) / clientWidth) * 2 - 1
  mouse.y = -((event.clientY - offsetTop) / clientHeight) * 2 + 1

  camera.updateMatrixWorld() //更新相机投影矩阵，防止鼠标点击时，模型未选中
  scene.updateMatrixWorld()

  raycaster.setFromCamera(mouse, camera)
  const intersects = raycaster.intersectObjects(scene.children, true)
  if (intersects.length > 0) {
    const selectedObject = intersects[0].object
    const worldPosition = new THREE.Vector3()
    selectedObject.getWorldPosition(worldPosition)

    createTag(selectedObject, '', worldPosition)

    outlinePass.selectedObjects = [selectedObject]
  } else {
    outlinePass.selectedObjects = []
  }
}

const modelId = ref([])
// 加载模型
function loadModel(url, id) {
  const loader = new GLTFLoader()
  loader.load(
    url,
    (gltf) => {
      const object = gltf.scene

      modelId.value.push(object.id)
      object.updateMatrixWorld()
      object.visible = id === 0
      object.userData.id = id // 将 id 存储在 userData 中
      const box = new THREE.Box3().setFromObject(object)
      const center = box.getCenter(new THREE.Vector3())
      object.position.x += object.position.x - center.x
      object.position.y += object.position.y - center.y
      object.position.z += object.position.z - center.z
      object.scale.set(1, 1, 1)
      scene.add(object)
      showInfo.value = true
      showPercentage.value = false
    },
    (xhr) => {
      percentage.value = (xhr.loaded / xhr.total) * 100
    }
  )
}

// 加载全部模型
function loadAllModel() {
  mainModel.forEach((model) => loadModel(model.url, model.id))
}

// 计算 容器元素 高宽，左偏移值，顶偏移值
function updateContainerElement(containerElement) {
  const rect = containerElement.getBoundingClientRect()
  offsetLeft = rect.left
  offsetTop = rect.top
}

// 设置窗口大小变化时的处理函数
function setupWindowResizeHandler(camera, renderer) {
  function onWindowResize() {
    const newWidth = window.innerWidth
    const newHeight = window.innerHeight
    camera.aspect = newWidth / newHeight
    camera.updateProjectionMatrix()
    renderer.setSize(newWidth, newHeight)
    css2DRenderer.setSize(newWidth, newHeight) // 更新 CSS2DRenderer 尺寸
  }
  window.addEventListener('resize', onWindowResize)
}

// 检测是否包含子对象
function checkContainsObject(object, targetObject) {
  for (const child of object.children) {
    if (child.name === targetObject) {
      return true
    }
    if (child.children.length > 0) {
      if (checkContainsObject(child, targetObject)) {
        return true
      }
    }
  }
  return false
}
</script>

<style scoped lang=scss >
.contianer {
  position: fixed;
  top: 0;
  left: 0;

  .floor-contianer {
    margin: 100px;
    display: flex;
    flex-direction: column;
    gap: 30px;
  }

  .btns {
    position: fixed;
    bottom: 100px;
    left: 40%;
  }

  .car-model {
    width: 200px;
    height: 120px;
    background-color: #fff;
    color: #333;
    position: fixed;
    right: 30%;
    top: 200px;
  }
}
.el-icon {
  margin-right: 10px;
  cursor: pointer;
  color: red;
}
.percentage {
  width: 300px;
  position: fixed;
  bottom: 45%;
  left: 40%;
  color: #fff;
}
:deep(.el-progress__text) {
  color: #fff;
}

.B1-contaner {
  position: fixed;
  top: 0;
  left: 100px;
  margin: 100px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  margin: 100px;

  .videos {
    /* color: #fff; */
  }
}
</style>
